package com.engine.jucailinkq.attendance.workflow.service.impl;

import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.jucailinkq.attendance.attendanceanalysis.service.UtilService;
import com.engine.jucailinkq.attendance.attendanceanalysis.service.impl.UtilServiceImpl;
import com.engine.jucailinkq.attendance.enums.*;
import com.engine.jucailinkq.attendance.workflow.service.OvertimePlanService;
import com.engine.jucailinkq.common.util.CommonUtil;
import com.engine.jucailinkq.common.util.DateUtil;
import com.engine.jucailinkq.common.util.DbTools;
import com.engine.jucailinkq.common.util.Utils;
import lombok.extern.slf4j.Slf4j;
import weaver.general.TimeUtil;
import weaver.general.Util;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: sy
 * @Description: 加班计划流程相关方法实现类
 * @Date: 2024/7/18
 **/
@Slf4j
public class OvertimePlanServiceImpl extends Service implements OvertimePlanService {

    private UtilService utilService = ServiceUtil.getService(UtilServiceImpl.class);

    @Override
    public Map<String, Object> generateOvertimePlanDetails(Map<String, Object> params) {
        Map<String,Object> resultMap = new HashMap<>();
        List<String> errorMessage = new ArrayList<>();
        try {
            // 流程表单主表数据
            Map<String,String> mainTableData = (Map<String,String>)params.get("mainTableData");
            log.info("mainTableData : {}", mainTableData);
            //加班人员、开始日期、结束日期、开始时间、结束时间
            String jbry = Util.null2String(mainTableData.get("jbry"));
            String startDate = Util.null2String(mainTableData.get("ksrq"));
            String endDate = Util.null2String(mainTableData.get("jsrq"));
            String startTime = Util.null2String(mainTableData.get("kssj"));
            String endTime = Util.null2String(mainTableData.get("jssj"));
            //获取加班人员列表
            List<String> empIdList = "".equals(jbry) ? new ArrayList<>() : Arrays.asList(jbry.split(","));
            //获取加班日期集合
            List<String> dateList = DateUtil.getDatesBetween(startDate, endDate);
            //获取人员列表在日期区间的班次数据
            Map<String, List<Map<String, Object>>> scheduleInfoMap = utilService.getScheduleInfoWithEmpId(empIdList, DateUtil.beforeDay(startDate, 1), DateUtil.AfterDay(endDate,1));
            List<Map<String, String>> overtimePlanDetailList = new ArrayList<>();
            List<String> workBdlxList = new ArrayList<>();
            workBdlxList.add(ClassSegmentTypeEnum.WORK_TIME.getKey());workBdlxList.add(ClassSegmentTypeEnum.EXTENDED_OVERTIME.getKey());
            workBdlxList.add(ClassSegmentTypeEnum.EARLY_OVERTIME.getKey());workBdlxList.add(ClassSegmentTypeEnum.OVERTIME_IN_CLASS.getKey());
            Map<String, String> detailItem;
            String sql = "";
            //获取作用时段包含计划加班的加班类型的考勤项目集合
            sql = "select id,mc, bddrqlx, jbqsfzs, xzzjbsc, rzdjbxss, zzdjbxss, yzdjbxss, zysd, ccclfs,zdkcjcxxsc from uf_jcl_kq_kqxm where xmlx = ?";
            List<Map<String, Object>> jblxAttendanceList = DbTools.getSqlToList(sql, AttendanceItemTypeEnum.WORK_OVERTIME.getKey());
            jblxAttendanceList = jblxAttendanceList.stream().filter(f -> Util.null2String(f.get("zysd")).contains(WorkForTimeEnum.PLAN_WORK_OVERTIME.getKey())).collect(Collectors.toList());
            //获取人员id和姓名信息
            Map<String, String> empIdToNameInfo = CommonUtil.empIdToNameInfo(empIdList);
            //获取目标人员列表已存在的加班计划
            Map<String, List<Map<String, Object>>> existOvertimePlanInfo = getOvertimePlanInfo(empIdList, startDate);
            List<Map<String, Object>> existOvertimePlanList = new ArrayList<>();

            for (String empId : empIdList) {
                List<Map<String, Object>> scheduleInfoList = scheduleInfoMap.get(empId);
                Map<String, String> dateToBcxxMap = scheduleInfoList == null ? new HashMap<>() : scheduleInfoList.stream().collect(Collectors.toMap(e->Util.null2String(e.get("bcrq")), e->Util.null2String(e.get("bcxx"))));
                Map<String, String> dateTypeInfoFromBc = scheduleInfoList == null ? new HashMap<>() : scheduleInfoList.stream().collect(Collectors.toMap(e->Util.null2String(e.get("bcrq")), e->Util.null2String(e.get("rqlx"))));
                existOvertimePlanList = existOvertimePlanInfo.get(empId);
                Map<String, Double> weekOvertimeInfo = new HashMap<>();
                Map<String, Double> monthOvertimeInfo = new HashMap<>();
                for (String date : dateList) {
                    String realEndDate = date;
                    detailItem = new HashMap<>();
                    detailItem.put("jbry", empId);
                    detailItem.put("jbryName", Util.null2String(empIdToNameInfo.get(empId)));
                    detailItem.put("ksrq", date);
                    detailItem.put("kssj", startTime);
                    detailItem.put("jssj", endTime);
                    detailItem.put("jblx", "");
                    detailItem.put("jblxName", "");
                    //开始时时和结束时间存在跨天情况时,即开始时间大于等于结束时间
                    if (startTime.compareTo(endTime) >= 0) {
                        realEndDate = DateUtil.AfterDay(date,1);
                    }
                    detailItem.put("jsrq", realEndDate);
                    //判断改组明细是否与已存在的加班计划有重叠
                    boolean overLappingSign = checkOverlapping(existOvertimePlanList, date + " " +startTime, realEndDate + " " + endTime);
                    if (overLappingSign) {
                        errorMessage.add(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + date + "的加班计划区间和已申请的加班计划（已审核/待审核）出现时间重叠，不允许申请加班！");
                        continue;
                    }
                    //加班时长
                    int overtimeMinutes = DateUtil.getBetWeenMinutes(date + " " + startTime, realEndDate + " " + endTime);
                    detailItem.put("jbsc", String.format("%.2f", overtimeMinutes / 60.0));
                    //根据班次数据，设置归属日期
                    //获取当天班次id
                    String currentDayBcId = Util.null2String(dateToBcxxMap.get(date)).split("-")[0];
                    //获取当天班次开始时间和结束时间
                    String currentDayStartToEnd = getBcStartAndEndTime(date, currentDayBcId, workBdlxList);
                    String currentDayStartTime = !"".equals(currentDayStartToEnd) ? currentDayStartToEnd.split(",")[0] : "";
                    String currentDayEndTime = !"".equals(currentDayStartToEnd) ? currentDayStartToEnd.split(",")[1] : "";
                    //判断当天班次是否为休息班次、且“休息班打卡自动加班”未勾选
                    boolean currentDayRestBc = checkRestBc(currentDayBcId);

                    //获取前1天班次id
                    String beforeDayBcId = Util.null2String(dateToBcxxMap.get(DateUtil.beforeDay(date, 1))).split("-")[0];
                    //获取前1天班次开始时间和结束时间
                    String beforeDayStartToEnd = getBcStartAndEndTime(DateUtil.beforeDay(date, 1), beforeDayBcId, workBdlxList);
                    String beforeDayStartTime = !"".equals(beforeDayStartToEnd) ? beforeDayStartToEnd.split(",")[0] : "";
                    String beforeDayEndTime = !"".equals(beforeDayStartToEnd) ? beforeDayStartToEnd.split(",")[1] : "";
                    //判断前1天班次是否为休息班次、且“休息班打卡自动加班”未勾选
                    boolean beforeDayRestBc = checkRestBc(beforeDayBcId);

                    //获取次日班次id
                    String nextDayBcId = Util.null2String(dateToBcxxMap.get(DateUtil.AfterDay(date, 1))).split("-")[0];
                    //获取次日班次开始时间和结束时间
                    String nextDayStartToEnd = getBcStartAndEndTime(DateUtil.AfterDay(date, 1), nextDayBcId, workBdlxList);
                    String nextDayStartTime = !"".equals(nextDayStartToEnd) ? nextDayStartToEnd.split(",")[0] : "";
                    String nextDayEndTime = !"".equals(nextDayStartToEnd) ? nextDayStartToEnd.split(",")[1] : "";
                    //判断次日班次是否为休息班次、且“休息班打卡自动加班”未勾选
                    boolean nextDayRestBc = checkRestBc(nextDayBcId);

                    //梳理加班时间段和班段时间段合理的情况
                    boolean beforeToCurrentDay = false;
                    boolean currentToNextDay = false;
                    boolean beforeToNextDay = false;
                    String belongDate = "";
                    //20240812需求变更，休息班次，允许加班计划区间和班次班段区间有重叠
                    boolean currentDayOverLap = false;
                    boolean beforeDayOverLap = false;
                    boolean nextDayOverLap = false;
                    //20240820需求变更，非休息班次，加班区间完全处于休息班段内时，允许申请加班计划，并在匹配加班类型时避开勾选了去除休息时段的加班类型
                    boolean onlyInRestPeriod = false;
                    //20240814需求变更。非休息班次，出现重叠时间，自动调整加班开始或结束时间，并返回提示
                    Map<String, String> adjustInfo = new HashMap<>();
                    if (!"".equals(currentDayStartToEnd)) {
                        //1-加班时间段处于前一日下班后和当日上班前
                        beforeToCurrentDay = ("".equals(beforeDayEndTime) || beforeDayEndTime.compareTo(date + " " + startTime) <= 0) && currentDayStartTime.compareTo(realEndDate + " " + endTime) >=0;
                        //2-加班时间段处于当日下班后和次日上班前
                        currentToNextDay = ("".equals(nextDayStartTime) || nextDayStartTime.compareTo(realEndDate + " " + endTime) >= 0) && currentDayEndTime.compareTo(date + " " + startTime) <=0;
                    } else {
                        //3-加班时间段处于前一日下班后和次日上班前
                        beforeToNextDay = ("".equals(beforeDayEndTime) || beforeDayEndTime.compareTo(date + " " + startTime) <= 0)
                                && ("".equals(nextDayStartTime) || nextDayStartTime.compareTo(realEndDate + " " + endTime) >= 0);
                    }
                    if (beforeToCurrentDay) {
                        int beforeDayDiffMinutes = !"".equals(beforeDayEndTime) ? DateUtil.getBetWeenMinutes(beforeDayEndTime, date + " " + startTime) : 9999;
                        int currentDayDiffMinutes = DateUtil.getBetWeenMinutes(realEndDate + " " + endTime, currentDayStartTime);
                        belongDate = currentDayDiffMinutes < beforeDayDiffMinutes ? date : DateUtil.beforeDay(date, 1);
                    } else if (currentToNextDay) {
                        int nextDayDiffMinutes = !"".equals(nextDayStartTime) ? DateUtil.getBetWeenMinutes(realEndDate + " " + endTime, nextDayStartTime) : 9999;
                        int currentDayDiffMinutes = DateUtil.getBetWeenMinutes(currentDayEndTime, date + " " + startTime);
                        belongDate = currentDayDiffMinutes <= nextDayDiffMinutes ? date : DateUtil.AfterDay(date, 1);
                    } else if (beforeToNextDay) {
                        int beforeDayDiffMinutes = !"".equals(beforeDayEndTime) ? DateUtil.getBetWeenMinutes(beforeDayEndTime, date + " " + startTime) : 9999;
                        int nextDayDiffMinutes = !"".equals(nextDayStartTime) ? DateUtil.getBetWeenMinutes(realEndDate + " " + endTime, nextDayStartTime) : 9999;
                        if (beforeDayDiffMinutes > 120 && nextDayDiffMinutes > 120) {
                            //当天没班次时，前后两天班次班段与加班区间相隔超过120分钟时，则归属日为请假开始日期
                            belongDate = date;
                        } else {
                            belongDate = beforeDayDiffMinutes <= nextDayDiffMinutes ? DateUtil.beforeDay(date, 1) : DateUtil.AfterDay(date, 1);
                        }
                    } else {
                        //20240812需求变更，休息班次，允许加班计划区间和班次班段区间有重叠
                        currentDayOverLap = !"".equals(currentDayStartToEnd) && DateUtil.isOverlapping(date + " " + startTime, realEndDate + " " + endTime, currentDayStartTime, currentDayEndTime);
                        beforeDayOverLap = !"".equals(beforeDayStartToEnd) && DateUtil.isOverlapping(date + " " + startTime, realEndDate + " " + endTime, beforeDayStartTime, beforeDayEndTime);
                        nextDayOverLap = !"".equals(nextDayStartToEnd) && DateUtil.isOverlapping(date + " " + startTime, realEndDate + " " + endTime, nextDayStartTime, nextDayEndTime);
                        //20240814需求变更。非休息班次，出现重叠时间，自动调整加班开始或结束时间，并返回提示
                        adjustInfo.put("startWithAdjust", date + " " + startTime);
                        adjustInfo.put("endWithAdjust", realEndDate + " " + endTime);
                        adjustInfo.put("startDate", date);
                        adjustInfo.put("startTime", startTime);
                        adjustInfo.put("endDate", realEndDate);
                        adjustInfo.put("endTime", endTime);
                        if ((!currentDayRestBc && currentDayOverLap) || (!beforeDayRestBc && beforeDayOverLap) || (!nextDayRestBc && nextDayOverLap)) {
                            //处理前一天
                            if(!beforeDayRestBc && beforeDayOverLap) {
                                belongDate = DateUtil.beforeDay(date, 1);
                                if (overtimeOnlyInRestRange(beforeDayBcId, date + " " + startTime, realEndDate + " " + endTime, DateUtil.beforeDay(date, 1))) {
                                    onlyInRestPeriod = true;
                                } else {
                                    adjustInfo = doAdjust(adjustInfo, beforeDayStartTime, beforeDayEndTime);
                                }
                            }
                            //处理当天
                            if(!currentDayRestBc && currentDayOverLap) {
                                belongDate = "".equals(belongDate) ? date : belongDate;
                                if (overtimeOnlyInRestRange(currentDayBcId, date + " " + startTime, realEndDate + " " + endTime, date)) {
                                    onlyInRestPeriod = true;
                                } else {
                                    adjustInfo = doAdjust(adjustInfo, currentDayStartTime, currentDayEndTime);
                                }
                            }
                            //处理次日
                            if(!nextDayRestBc && nextDayOverLap) {
                                belongDate = "".equals(belongDate) ? DateUtil.AfterDay(date, 1) : belongDate;
                                if (overtimeOnlyInRestRange(nextDayBcId, date + " " + startTime, realEndDate + " " + endTime, DateUtil.AfterDay(date, 1))) {
                                    onlyInRestPeriod = true;
                                } else {
                                    adjustInfo = doAdjust(adjustInfo, nextDayStartTime, nextDayEndTime);
                                }
                            }
                            //重置加班开始和结束时间、加班时长
                            detailItem.put("ksrq", adjustInfo.get("startDate"));
                            detailItem.put("kssj", adjustInfo.get("startTime"));
                            detailItem.put("jssj", adjustInfo.get("endTime"));
                            detailItem.put("jsrq", adjustInfo.get("endDate"));
                            overtimeMinutes = DateUtil.getBetWeenMinutes(adjustInfo.get("startWithAdjust"), adjustInfo.get("endWithAdjust"));
                            //4-非休息班次，出现重叠时间，属于不合理加班安排，返回提示”已自动调整开始和结束时间点“
                            if (!onlyInRestPeriod) {
                                errorMessage.add(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + date + "的加班计划区间和非休息班次班段出现时间重叠，已自动调整开始和结束时间点！");
                            }

                        }
                        //5-加班区间和休息班次出现重叠，首次重叠的日期，即为归属日期;如果已有归属日，则比较当前日期和归属日谁更早
                        if (beforeDayRestBc && beforeDayOverLap) {
                            belongDate = DateUtil.beforeDay(date, 1);
                        } else if (currentDayRestBc && currentDayOverLap) {
                            belongDate = "".equals(belongDate) || date.compareTo(belongDate) < 0 ? date : belongDate;
                        } else if (nextDayRestBc && nextDayOverLap) {
                            belongDate = "".equals(belongDate) ? DateUtil.AfterDay(date, 1) : belongDate;
                        }
                    }
                    //判断归属日的日期类型,首先从排班的信息来获取
                    String belongDateType = dateTypeInfoFromBc.getOrDefault(belongDate, "");
                    //排班信息无法获取日期类型时，从日历信息获取
                    belongDateType = "".equals(belongDateType) ? CommonUtil.getRqlx(empId, belongDate) : belongDateType;
                    //排班和日历都无法获取时，默认为工作日
                    belongDateType = "".equals(belongDateType) ? DateTypeEnum.WORK_DAY.getKey() : belongDateType;

                    //通过人员id、归属日期、归属日期类型来自动匹配加班类型
                    //获取人员在归属日被设置的考勤项目
                    List<Map<String, Object>> attendanceItemSetList = CommonUtil.getAttendanceItemsByEmpIdDate(empId, belongDate);
                    List<String> kqxmSetIds = attendanceItemSetList.stream().map(f->f.get("keyid").toString()).collect(Collectors.toList());
//                    log.info(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + date + "的可用考勤项目：" + kqxmSetIds);
                    Map<String, Object> matchItemInfo = new HashMap<>();
                    for (Map<String, Object> attendanceItemInfo : jblxAttendanceList) {
                        //判断该加班类型考勤项目是否被该人员使用，且考勤项目的绑定的日期类型是否包含归属日期类型
                        if (kqxmSetIds.contains(Util.null2String(attendanceItemInfo.get("id")))
                                && Util.null2String(attendanceItemInfo.get("bddrqlx")).contains(belongDateType)) {
                            //非休息班次，加班区间完全处于休息班段内时，在匹配加班类型时避开勾选了“是否扣除时间区间内的就餐时长”的加班类型
                            if (onlyInRestPeriod && "1".equals(Util.null2String(attendanceItemInfo.get("zdkcjcxxsc")))) {
                                continue;
                            }
                            detailItem.put("jblx", Util.null2String(attendanceItemInfo.get("id")));
                            detailItem.put("jblxName", Util.null2String(attendanceItemInfo.get("mc")));
                            matchItemInfo = attendanceItemInfo;
                        }
                    }
                    detailItem.put("gsrq", belongDate);
                    //20240814需求变更。增加单条明细的最小加班分钟数校验，工作日加班最大小时数、周加班最大小时数、月加班最大小时数
                    if (matchItemInfo.size() == 0) {
                        log.info(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + date + "的加班计划未匹配到合适的加班类型！");
                        errorMessage.add(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + date + "的加班计划未匹配到合适的加班类型！");
                        continue;
                    } else {
                        log.info(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + date + "的加班计划匹配到:" + Util.null2String(matchItemInfo.get("mc")));
                        //20240816需求变更，根据zdkcjcxxsc字段设置，即“是否扣除时间区间内的就餐时长”需要去除重叠的休息班次中的休息时段时长
                        String removeRestSc = Util.null2String(matchItemInfo.get("zdkcjcxxsc"));
                        int restOverLapMinutes = 0;
                        if (beforeDayRestBc && beforeDayOverLap && "1".equals(removeRestSc)) {
                            restOverLapMinutes = restOverLapMinutes
                                    + countRestOverLapMinutes(beforeDayBcId, adjustInfo.get("startWithAdjust"), adjustInfo.get("endWithAdjust"), DateUtil.beforeDay(date, 1));
                        } else if (currentDayRestBc && currentDayOverLap && "1".equals(removeRestSc)) {
                            restOverLapMinutes = restOverLapMinutes
                                    + countRestOverLapMinutes(currentDayBcId, adjustInfo.get("startWithAdjust"), adjustInfo.get("endWithAdjust"), date);
                        } else if (nextDayRestBc && nextDayOverLap && "1".equals(removeRestSc)) {
                            restOverLapMinutes = restOverLapMinutes
                                    + countRestOverLapMinutes(nextDayBcId, adjustInfo.get("startWithAdjust"), adjustInfo.get("endWithAdjust"), DateUtil.AfterDay(date, 1));
                        }
                        overtimeMinutes = overtimeMinutes - restOverLapMinutes;
                        detailItem.put("jbsc", String.format("%.2f", overtimeMinutes / 60.0));
                        //单条明细的最小加班分钟数校验
                        String minMinutes = Util.null2String(matchItemInfo.get("jbqsfzs"));
                        if (!"".equals(minMinutes) && Integer.parseInt(minMinutes) > overtimeMinutes) {
                            //最小加班分钟数大于单条明细的加班时长分钟数
                            errorMessage.add(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + date + "的加班分钟数小于加班类型-"
                                    + Util.null2String(matchItemInfo.get("mc")) +"设置的最小加班分钟数！");
                            continue;
                        }
                        String limitTotalOvertimeSc = Util.null2String(matchItemInfo.get("xzzjbsc"));
                        if ("1".equals(limitTotalOvertimeSc)) {
                            String limitWorkDayHours = Util.null2String(matchItemInfo.get("rzdjbxss"));
                            String limitWeekHours = Util.null2String(matchItemInfo.get("zzdjbxss"));
                            String limitMonthHours = Util.null2String(matchItemInfo.get("yzdjbxss"));
                            String limitDealType = Util.null2String(matchItemInfo.get("ccclfs"));
                            boolean doLimitWorkDayHours = false;
                            boolean doLimitWeekHours = false;
                            boolean doLimitMonthHours = false;
                            //判断是否满足工作日加班最大小时数
                            boolean needCheckWorkDayHours = !"".equals(limitWorkDayHours) && (belongDateType.equals(DateTypeEnum.WORK_DAY.getKey()) || belongDateType.equals(DateTypeEnum.CHANGECLASS.getKey()));
                            if (needCheckWorkDayHours && Double.compare(Double.parseDouble(limitWorkDayHours), overtimeMinutes / 60.0) < 0) {
                                doLimitWorkDayHours = true;
                                errorMessage.add(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + date + "的加班时长累计后超过了加班类型-"
                                        + Util.null2String(matchItemInfo.get("mc")) +"设置的工作日加班最大小时数！");
                            }
                            //判断是否满足周加班最大小时数
                            if (!"".equals(limitWeekHours)) {
                                int weekRank = DateUtil.weekRank(belongDate);
                                double maxWeekMinutes = Double.parseDouble(limitWeekHours) *60;
                                double weekOvertimeMinutes;
                                if (weekOvertimeInfo.get(belongDate.split("-")[0] + "" + weekRank) == null) {
                                    String countStartDate = DateUtil.beforeDay(belongDate.split("-")[0]+"-"+ belongDate.split("-")[1]+"-01", 6);
                                    String countEndDate = DateUtil.AfterDay(belongDate.split("-")[0]+"-"+ belongDate.split("-")[1]+"-28", 9);
                                    weekOvertimeMinutes = getWeekTimeMinutes(getWorkOverTimeResults(countStartDate, countEndDate, empId), belongDate);
                                } else {
                                    weekOvertimeMinutes = weekOvertimeInfo.get(belongDate.split("-")[0] + "" + weekRank);
                                }
                                weekOvertimeInfo.put(belongDate.split("-")[0] + "" + weekRank, weekOvertimeMinutes + overtimeMinutes);
                                if (maxWeekMinutes - weekOvertimeMinutes - overtimeMinutes < 0) {
                                    //达到周加班最大小时数
                                    doLimitWeekHours = true;
                                    errorMessage.add(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + date + "的加班时长累计后超过了加班类型-"
                                            + Util.null2String(matchItemInfo.get("mc")) +"设置的周加班最大小时数！");
                                }
                            }
                            //判断是否满足月加班最大小时数
                            if (!"".equals(limitMonthHours)) {
                                String yearMonth = belongDate.substring(0, 7);
                                double maxMonthMinutes = Double.parseDouble(limitMonthHours) *60;
                                double monthOvertimeMinutes;
                                if (monthOvertimeInfo.get(yearMonth) == null) {
                                    String countStartDate = belongDate.split("-")[0]+"-"+ belongDate.split("-")[1]+"-01";
                                    String countEndDate = belongDate.split("-")[0]+"-"+ belongDate.split("-")[1]+"-31";
                                    monthOvertimeMinutes = getMonthTimeMinutes(getWorkOverTimeResults(countStartDate, countEndDate, empId), belongDate);
                                } else {
                                    monthOvertimeMinutes = monthOvertimeInfo.get(yearMonth);
                                }
                                monthOvertimeInfo.put(yearMonth, monthOvertimeMinutes + overtimeMinutes);
                                if (maxMonthMinutes - monthOvertimeMinutes - overtimeMinutes < 0) {
                                    //达到月加班最大小时数
                                    doLimitMonthHours = true;
                                    errorMessage.add(Util.null2String(empIdToNameInfo.get(empId)) + "在日期" + date + "的加班时长累计后超过了加班类型-"
                                            + Util.null2String(matchItemInfo.get("mc")) +"设置的月加班最大小时数！");
                                }
                            }
//                            //判断是否超出工作日、周、月最大小时数要求
//                            if ("1".equals(limitDealType) && (doLimitWorkDayHours || doLimitWeekHours || doLimitMonthHours)) {
//                                continue;
//                            }
                        }
                    }
                    overtimePlanDetailList.add(detailItem);
                }
            }
            if (errorMessage.size() == 0) {
                resultMap.put("status", true);
            } else {
                resultMap.put("status", false);
                resultMap.put("errorInfo", errorMessage);
            }
            resultMap.put("data", overtimePlanDetailList);

        } catch (Exception e) {
            log.info(e.getMessage());
            resultMap.put("status", false);
            resultMap.put("errorInfo", e.getMessage());
            resultMap.put("data", null);
        }
        return resultMap;
    }

    /**
     * 目标区间和日期所在班次的休息时段重叠的分钟数
     */
    private int countRestOverLapMinutes(String dayBcId, String startTime, String endTime, String date) {
        List<String> countBdlxList = new ArrayList<>();
        countBdlxList.add(ClassSegmentTypeEnum.REST_AND_DINE.getKey());
        countBdlxList.add(ClassSegmentTypeEnum.REST_PERIOD.getKey());
        countBdlxList.add(ClassSegmentTypeEnum.DINING_PERIOD.getKey());
        String sql = "select id, bdlx, gsrq, kssj as dtkssj, jssj as dtjssj from uf_jcl_kq_bcxx_dt1 where mainid  = " + dayBcId;
        List<Map<String, Object>> bcDetailData = DbTools.getSqlToList(sql);
        bcDetailData = bcDetailData.stream().filter(e -> countBdlxList.contains(Util.null2String(e.get("bdlx")))).collect(Collectors.toList());
        //获取需要累计的班段时长区间和目标区间存在交集的分钟数
        return Utils.removeTime(startTime, endTime, bcDetailData, date);
    }

    /**
     * 加班区间和非休息班次出现重叠时，自动调整加班区间的开始/结束时间
     */
    private Map<String, String> doAdjust(Map<String, String> adjustInfo, String bcStartTime, String bcEndTime) {
        String startWithAdjust = adjustInfo.get("startWithAdjust");
        String endWithAdjust = adjustInfo.get("endWithAdjust");
        //加班区间完全包含了班次区间时
        if (bcStartTime.compareTo(startWithAdjust) > 0  && bcEndTime.compareTo(endWithAdjust) < 0) {
            startWithAdjust = bcEndTime;
        } else {
            //加班区间与部分班次区间重叠时，此处可能出现加班区间完全被班次区间包含情况，从而导致加班开始时间晚于加班结束时间
            startWithAdjust = bcStartTime.compareTo(startWithAdjust) <= 0 && bcEndTime.compareTo(startWithAdjust) > 0
                    ? bcEndTime : startWithAdjust;
            endWithAdjust = bcStartTime.compareTo(endWithAdjust) < 0 && bcEndTime.compareTo(endWithAdjust) >= 0
                    ? bcStartTime : endWithAdjust;
        }
        //当加班开始时间晚于加班结束时间时
        endWithAdjust = startWithAdjust.compareTo(endWithAdjust) > 0 ? startWithAdjust : endWithAdjust;

        adjustInfo.put("startWithAdjust", startWithAdjust);
        adjustInfo.put("endWithAdjust", endWithAdjust);
        adjustInfo.put("startDate", startWithAdjust.split(" ")[0]);
        adjustInfo.put("startTime", startWithAdjust.split(" ")[1]);
        adjustInfo.put("endDate", endWithAdjust.split(" ")[0]);
        adjustInfo.put("endTime", endWithAdjust.split(" ")[1]);
        return adjustInfo;
    }

    /**
     * 判断请假区间是否完全处于班次中的休息时段内
     */
    private boolean overtimeOnlyInRestRange(String bcId, String startTime, String endTime, String date) {
        int overRangeMinutes = DateUtil.getBetWeenMinutes(startTime, endTime);
        List<String> countBdlxList = new ArrayList<>();
        countBdlxList.add(ClassSegmentTypeEnum.REST_AND_DINE.getKey());
        countBdlxList.add(ClassSegmentTypeEnum.REST_PERIOD.getKey());
        countBdlxList.add(ClassSegmentTypeEnum.DINING_PERIOD.getKey());
        String sql = "select id, bdlx, gsrq, kssj as dtkssj, jssj as dtjssj from uf_jcl_kq_bcxx_dt1 where mainid  = " + bcId;
        List<Map<String, Object>> bcDetailData = DbTools.getSqlToList(sql);
        bcDetailData = bcDetailData.stream().filter(e -> countBdlxList.contains(Util.null2String(e.get("bdlx")))).collect(Collectors.toList());
        //获取需要累计的班段时长区间和目标区间存在交集的分钟数，并与请假区间内的分钟数最比较
        return overRangeMinutes == Utils.removeTime(startTime, endTime, bcDetailData, date);
    }

    private boolean checkRestBc(String bcId) {
        boolean restSign = false;
        if (!"".equals(bcId)) {
            //查询当天班次明细
            String sql = "select id, sfxx, xxbdkzdjb from uf_jcl_kq_bcxx where id = " + bcId;
            Map<String, Object> data = DbTools.getSqlToMap(sql);
            if ("1".equals(Util.null2String(data.get("sfxx"))) && !"1".equals(Util.null2String(data.get("xxbdkzdjb")))) {
                restSign = true;
            }
        }
        return restSign;
    }

    private boolean checkOverlapping(List<Map<String, Object>> existOvertimePlanList, String startTimePoint, String endTimePoint) {
        boolean overlappingSign = false;
        if (existOvertimePlanList != null && existOvertimePlanList.size() > 0) {
            String contrastStartPoint = "";
            String contrastEndPoint = "";
            for (Map<String, Object> item : existOvertimePlanList) {
                contrastStartPoint = item.get("ksrq") + " " + item.get("kssj");
                contrastEndPoint = item.get("jsrq") + " " + item.get("jssj");
                overlappingSign = DateUtil.isOverlapping(contrastStartPoint, contrastEndPoint, startTimePoint, endTimePoint);
                if (overlappingSign) {
                    break;
                }
            }
        }
        return overlappingSign;
    }

    /**
     * 获取目标人员在加班计划表中结束日期大于等于匹配日期的数据记录
     * @param empIdList 人员id列表
     * @param matchDate 匹配日期
     * @returnd
     */
    private Map<String, List<Map<String, Object>>> getOvertimePlanInfo(List<String> empIdList, String matchDate) {
        Map<String, List<Map<String, Object>>> result = new HashMap<>();
        if (empIdList.size() > 0 && !"".equals(matchDate)) {
            String sql = "select b.id, b.jbry, b.ksrq, b.jsrq, b.kssj, b.jssj from uf_jcl_kq_jbjh a left join uf_jcl_kq_jbjh_dt1 b on b.mainid = a.id where a.jlzt != 2 and b.jsrq >= '"
                    + matchDate + "' and b.jbry in (" + String.join(",",empIdList) + ")";
            List<Map<String, Object>> data = DbTools.getSqlToList(sql);
            result = data.stream().collect(Collectors.groupingBy(e->e.get("jbry").toString()));
        }
        return result;
    }

    public String getBcStartAndEndTime(String date, String currentDayBcId, List<String> workBdlxList) {
        String startToEnd = "";
        if (!"".equals(currentDayBcId)) {
            //查询当天班次明细
            String sql = "select id, bdlx, gsrq, kssj, jssj from uf_jcl_kq_bcxx_dt1 where mainid = " + currentDayBcId + " order by gsrq desc, kssj desc";
            List<Map<String, Object>> bcDetailData = DbTools.getSqlToList(sql);
            bcDetailData = bcDetailData.stream().filter(e -> workBdlxList.contains(Util.null2String(e.get("bdlx")))).collect(Collectors.toList());
            if (bcDetailData.size() > 0) {
                String endGsrqValue = Util.null2String(bcDetailData.get(0).get("gsrq"));
                String lastJssj = Util.null2String(bcDetailData.get(0).get("jssj"));
                String lastKssj = Util.null2String(bcDetailData.get(0).get("kssj"));
                String bdEndDate = "2".equals(endGsrqValue) ? DateUtil.AfterDay(date,1) : ("0".equals(endGsrqValue) ? DateUtil.beforeDay(date,1) : date);
                bdEndDate = lastKssj.compareTo(lastJssj) >= 0 ? DateUtil.AfterDay(bdEndDate,1) : bdEndDate;

                String startGsrqValue = Util.null2String(bcDetailData.get(bcDetailData.size() - 1).get("gsrq"));
                String firstKssj = Util.null2String(bcDetailData.get(bcDetailData.size() - 1).get("kssj"));
                String bdStartDate = "2".equals(startGsrqValue) ? DateUtil.AfterDay(date,1) : ("0".equals(startGsrqValue) ? DateUtil.beforeDay(date,1) : date);

                startToEnd = bdStartDate + " " + firstKssj + "," + bdEndDate + " " + lastJssj;
            }
        }
        return startToEnd;
    }

    /**
     * 获得一周加班分钟数
     * @param dataList
     * @return
     */
    public double getWeekTimeMinutes(List<Map<String, Object>> dataList,String date){
        int day = TimeUtil.getDayOfWeek(date);
        if (day ==0){
            day = 7;
        }
        String startDate = DateUtil.beforeDay(date,day-1);
        String endDate = DateUtil.AfterDay(date,7-day);
        List<Map<String, Object>> list = dataList.stream().filter(e->{
            String sjksrq = Util.null2String(e.get("sjksrq"));
            if (DateUtil.getTime(sjksrq).compareTo(DateUtil.getTime(startDate)) >=0 &&
                    DateUtil.getTime(sjksrq).compareTo(DateUtil.getTime(endDate)) <=0  &&
                    DateUtil.getTime(sjksrq).compareTo(DateUtil.getTime(date)) !=0){
                return true;
            }else {
                return false;
            }
        }).collect(Collectors.toList());

        double totalMinutes = 0;
        for (Map<String, Object> data:list){
            String hsdw = data.get("hsdw").toString();
            totalMinutes += Utils.getItemduration(1,AccountingUnitEnum.MINUTES.getKey(), Double.valueOf(data.get("sjjbsc").toString()),AccountingUnitEnum.getEnum(hsdw),8);
        }
        return totalMinutes;
    }

    /**
     * 获得一个月加班分钟数
     * @param dataList
     * @return
     */
    public double getMonthTimeMinutes(List<Map<String, Object>> dataList,String date){
        String startDate = date.split("-")[0]+"-"+ date.split("-")[1]+"-01";
        String endDate = date.split("-")[0]+"-"+ date.split("-")[1]+"-31";
        List<Map<String, Object>> list = dataList.stream().filter(e->DateUtil.getTime(e.get("sjksrq").toString()).compareTo(DateUtil.getTime(startDate))>=0 &&
                DateUtil.getTime(e.get("sjjsrq").toString()).compareTo(DateUtil.getTime(endDate))<=0).collect(Collectors.toList());
        double totalMinutes = 0;
        for (Map<String, Object> data:list){
            String hsdw = data.get("hsdw").toString();
            totalMinutes += Utils.getItemduration(1,AccountingUnitEnum.MINUTES.getKey(), Double.valueOf(data.get("sjjbsc").toString()),AccountingUnitEnum.getEnum(hsdw),8);
        }

        return totalMinutes;
    }

    /**
     * 获取目标人员时间区间内的加班结果
     */
    public List<Map<String, Object>> getWorkOverTimeResults(String startDate,String endDate,String userId){
        String sql = "select a.sjjbsc, a.sjksrq, a.sjjsrq, a.jblx, b.hsdw from uf_jcl_kq_jbjg a left join uf_jcl_kq_kqxm b on a.jblx = b.id where a.jbry=? and a.sjksrq>=? and a.sjjsrq<=?";
        List<Map<String, Object>> dataList = DbTools.getSqlToList(sql,userId,startDate,endDate);
        return dataList;
    }
}
